package com.elegant.code.spi.dao.mybatis;

import com.elegant.code.spi.context.SpringContexts;
import com.elegant.code.spi.dao.mybatis.annotation.*;
import com.elegant.code.spi.dao.mybatis.exception.NotFoundAuditingProvider;
import lombok.experimental.UtilityClass;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Created by dulei on 10/2/17.
 */
@UtilityClass
public class AuditingAction {


    public void markCreateAuditing(Object targetEntity) throws IllegalAccessException {
        boolean isList = List.class.isAssignableFrom(targetEntity.getClass());
        if(isList){
            List list = (List) targetEntity;
            if(CollectionUtils.isEmpty(list)){
                return;
            }
            for(Object target : list){
                doMarkCreateAudditingSingle(target);
            }
            return ;
        }

        doMarkCreateAudditingSingle(targetEntity);

    }

    private static void doMarkCreateAudditingSingle(Object targetEntity) throws IllegalAccessException {
        List<Field> fieldList = getFields(targetEntity);
        doMarkCreateAuditing(targetEntity, fieldList);
    }

    private static void doMarkCreateAuditing(Object targetEntity, List<Field> fieldList) throws IllegalAccessException {
        Date currentDate = new Date();
        for (Field field : fieldList) {
            if (AnnotationUtils.getAnnotation(field, CreateBy.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,getAuditingProvider().auditingUser());
            }
            if (AnnotationUtils.getAnnotation(field, CreateAt.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,currentDate);
            }

            if (AnnotationUtils.getAnnotation(field, UpdateBy.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,getAuditingProvider().auditingUser());
            }
            if (AnnotationUtils.getAnnotation(field, UpdateAt.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,currentDate);
            }

            if (AnnotationUtils.getAnnotation(field, AppID.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,getAuditingProvider().appId());
            }

            if (AnnotationUtils.getAnnotation(field, CompanyID.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,getAuditingProvider().companyId());
            }
        }
    }

    public void markUpdateAuditing(Object targetEntity) throws IllegalAccessException {
        doMarkUpdateAuditingSingle(targetEntity);
    }

    private static void doMarkUpdateAuditingSingle(Object targetEntity) throws IllegalAccessException {
        List<Field> fieldList = getFields(targetEntity);
        Date currentDate = new Date();
        for (Field field : fieldList) {
            if (AnnotationUtils.getAnnotation(field, UpdateBy.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,getAuditingProvider().auditingUser());
            }
            if (AnnotationUtils.getAnnotation(field, UpdateAt.class) != null) {
                ReflectionUtils.makeAccessible(field);
                ReflectionUtils.setField(field,targetEntity,currentDate);
            }
        }
    }

    private static List<Field> getFields(Object targetEntity) {
        List<Field> fieldList = new ArrayList<>();
        Class tempClass = targetEntity.getClass();
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        return fieldList;
    }

    private AuditingProvider getAuditingProvider() {
        return SpringContexts.getBeansByClass(AuditingProvider.class)
                .values()
                .stream()
                .filter(auditingProvider -> !SystemMetaObject.forObject(auditingProvider).hasGetter("h"))//不是MapperProxy的代理类
                .findFirst()
                .orElseThrow(NotFoundAuditingProvider::new);
    }

}
